package com.yuqih.common.service.impl;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import com.yuqih.common.annotation.FetchDirItemRule;
import com.yuqih.common.annotation.FetchManyRule;
import com.yuqih.common.annotation.FetchOneRule;
import com.yuqih.common.config.IFrameworkConfig;
import com.yuqih.common.dto.FieldDescriptor;
import com.yuqih.common.dto.FieldSpecifiedUtils;
import com.yuqih.common.dto.TypeDescriptor;
import com.yuqih.common.dto.ValueContainer;
import com.yuqih.common.service.IBaseService;
import com.yuqih.common.service.ICommonService;
import com.yuqih.common.service.IDirItemService;

abstract class QueryModelInterceptor implements MethodInterceptor, Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	// 要代理的原始对象
	// private final Class<?> clazz;
	private final TypeDescriptor modelDescriptor;
	private final Map<String, ValueContainer<Object>> valueMap = new HashMap<String, ValueContainer<Object>>();

	QueryModelInterceptor(TypeDescriptor modelDescriptor) {
		this.modelDescriptor = modelDescriptor;
	}

	private boolean needProxy(FieldDescriptor fieldDescriptor) {

		FetchOneRule fetchOneRule = fieldDescriptor.getAnnotation(FetchOneRule.class);
		if (fetchOneRule != null) {
			return true;
		}
		FetchManyRule fetchManyRule = fieldDescriptor.getAnnotation(FetchManyRule.class);
		if (fetchManyRule != null) {
			return true;
		}
		return false;
	}

	private FieldDescriptor findFieldDescriptor(Method method) {
		Collection<FieldDescriptor> fieldDescriptors = modelDescriptor.getFieldDescriptors();
		for (FieldDescriptor fieldDescriptor : fieldDescriptors) {

			if (Objects.equals(method, fieldDescriptor.getReadMethod())) {
				return fieldDescriptor;
			}

			if (Objects.equals(method, fieldDescriptor.getWriteMethod())) {
				return fieldDescriptor;
			}

		}
		return null;
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Override
	public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {

		FieldDescriptor fieldDescriptor = findFieldDescriptor(method);
		if (fieldDescriptor == null) {
			return proxy.invokeSuper(obj, args);
		}

		boolean needProxy = needProxy(fieldDescriptor);
		if (!needProxy) {
			return proxy.invokeSuper(obj, args);
		}

		if (Objects.equals(method, fieldDescriptor.getReadMethod())) {
			// get方法
			ValueContainer<Object> valueContainer = valueMap.get(fieldDescriptor.getFieldName());
			if (valueContainer == null) {
				// 查单个
				FetchOneRule fetchOneRule = fieldDescriptor.getAnnotation(FetchOneRule.class);
				if (fetchOneRule != null) {
					String propertyName = fetchOneRule.propertyName();

					Object result;
					Object p = PropertyUtils.getProperty(obj, propertyName);
					if (p == null) {
						result = null;
					} else {
						Class<?> type = fieldDescriptor.getFieldType();
						IBaseService baseService = getFrameworkConfig().getBaseService(type);
						if (baseService != null) {
							result = baseService.findById(p);
						} else {
							result = getCommonService().findById(type, p);
						}
					}
					valueContainer = new ValueContainer<Object>(result);
					valueMap.put(fieldDescriptor.getFieldName(), valueContainer);
					return result;
				} else {

					// 查多个
					FetchManyRule fetchManyRule = fieldDescriptor.getAnnotation(FetchManyRule.class);
					if (fetchManyRule != null) {
						Class<?> type = fieldDescriptor.getItemType();

						Class<?> conditionType = fetchManyRule.conditionType();
						String conditionPropertyName = fetchManyRule.conditionPropertyName();
						IBaseService baseService = getFrameworkConfig().getBaseService(type);

						Object condition = FieldSpecifiedUtils.createProxyInstance(conditionType);
						PropertyUtils.setProperty(condition, conditionPropertyName, PropertyUtils.getProperty(obj,
								modelDescriptor.getIdFieldDescriptor().getFieldName()));

						Object result;
						if (baseService != null) {
							result = baseService.list(condition);
						} else {
							result = getCommonService().list(type, condition);
						}
						valueContainer = new ValueContainer<Object>(result);
						valueMap.put(fieldDescriptor.getFieldName(), valueContainer);
						return result;
					} else {
						FetchDirItemRule fetchDirItemRule = fieldDescriptor.getAnnotation(FetchDirItemRule.class);
						// TODO 未完成
						String code = (String) PropertyUtils.getProperty(obj, fetchDirItemRule.propertyName());
						if (StringUtils.isBlank(code)) {
							return null;
						}
						Object result = getDirItemService().findByCode(fetchDirItemRule.dirId(), code);
						valueContainer = new ValueContainer<Object>(result);
						valueMap.put(fieldDescriptor.getFieldName(), valueContainer);
						return result;
					}
				}

			}
			return valueContainer.getValue();
		} else {
			// set方法
			ValueContainer<Object> valueContainer = valueMap.get(fieldDescriptor.getFieldName());
			if (valueContainer == null) {
				valueContainer = new ValueContainer<Object>(args[0]);
				valueMap.put(fieldDescriptor.getFieldName(), valueContainer);
			} else {
				valueContainer.setValue(args[0]);
			}

			return null;
		}

	}



	abstract IDirItemService getDirItemService();

	abstract IFrameworkConfig getFrameworkConfig();
	
	abstract ICommonService getCommonService();

}
